#include <app.h>
#include <shader.h>
#include <vector>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "color_shader.hpp"
#include "lua_mgr.hpp"
#include "points.hpp"

static sg::color_shader _colorshader;
static sg::points _points;
static sg::lua_mgr _luamgr;

static struct {
	double prevx, prevy;
	float rotatex, rotatey; // 分别表示绕 y 轴旋转时水平移动距离，绕 x 轴旋转时垂直移动距离
	bool isclick;
} _params;

static void 
cb_key(GLFWwindow* window, int key, int scancode, int action, int mods) {
    if (action) {
        switch (key) {
            case 'R': {
            	_params.rotatex = _params.rotatey = 0.0f;

            	std::vector<glm::vec3> vertices;
				_luamgr.getpoints(vertices);
				_points.init(vertices);
            	break;
            }
        }
    }
}

static void 
cb_mouse(GLFWwindow *wnd, int buttn, int action, int mods) {
	if (action == GLFW_PRESS) {
		_params.isclick = true;
		glfwGetCursorPos(wnd, &_params.prevx, &_params.prevy);

	} else {
		_params.isclick = false;
	}
}

static void
cb_cursor_pos(GLFWwindow *wnd, double x, double y) {
	if (!_params.isclick)
		return;
	double sx, sy;
	glfwGetCursorPos(wnd, &sx, &sy);

	float deltax = (float)(sx - _params.prevx) / 16.0f;
	float deltay = (float)(sy - _params.prevy) / 16.0f;
	_params.rotatex += deltax;
	_params.rotatey += deltay;
	_params.rotatey = _params.rotatey < -90.0f ? -90.0f : _params.rotatey;
	_params.rotatey = _params.rotatey > 90.0f ? 90.0f : _params.rotatey;
}

static GLboolean
cb_init(GLFWwindow *wnd) {
	glfwSetKeyCallback(wnd, cb_key);
	glfwSetMouseButtonCallback(wnd, cb_mouse);
	glfwSetCursorPosCallback(wnd, cb_cursor_pos);

	glEnable(GL_DEPTH_TEST);

	if (!_colorshader.init()) {
		printf("fail to init shader\n");
		return GL_FALSE;
	}

	_luamgr.init();

	std::vector<glm::vec3> vertices;
	_luamgr.getpoints(vertices);
	_points.init(vertices);
   	return GL_TRUE;
}

static glm::mat4
get_view_mat() {
	glm::mat4 viewmat = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -10.0f));
	viewmat = glm::rotate(viewmat, glm::radians(-_params.rotatey), glm::vec3(1.0f, 0, 0));
	viewmat = glm::rotate(viewmat, glm::radians(-_params.rotatex), glm::vec3(0, 1.0f, 0));
   	return viewmat;
}

static void
cb_render(GLFWwindow *wnd, double current, int w, int h) {
    static GLfloat black[] = {0, 0, 0, 0};
    glClearBufferfv(GL_COLOR, 0, black);
    static GLfloat one = 1.0f;
    glClearBufferfv(GL_DEPTH, 0, &one);
    glPointSize(5.0f);

    glm::mat4 viewmat = get_view_mat();
    glm::mat4 projmat = glm::perspective(glm::radians(60.0f), (float)w / (float)h, 1.0f, 100.0f);
    glm::mat4 mvp = projmat * viewmat;

    _colorshader.useprogram();
    
    _colorshader.setuniform(mvp, 0.0f, 0.0f, 1.0f);
    glDepthMask(GL_FALSE);
    _points.draw();
    glDepthMask(GL_TRUE);

    _colorshader.setuniform(mvp, 1.0f, 0.0f, 0.0f);
    _points.drawsingle(current);

    fflush(stdout);
}

static void
cb_destroy() {

}

int 
main() {
	if (!app_init(800, 600, "A simple graph tool", cb_init))
		return -1;

	app_run(cb_render);
	app_destroy(cb_destroy);

	return 0;
}
